<?php
/**
 * Holds class Image_Image
 *
 * @author daantimmer
 *
 * @package Library
 * @subpackage Image
 */

/**
 * Red offset
 *
 * @var integer
 */
define('R',0);

/**
 * Green offset
 *
 * @var integer
 */
define('G',1);

/**
 * Blue offset
 *
 * @var integer
 */
define('B',2);

/**
 * Hue offset
 *
 * @var integer
 */
define('H',0);

/**
 * Saturation offset
 *
 * @var integer
 */
define('S',1);

/**
 * Value offset
 *
 * @var integer
 */
define('V',2);

/**
 * Pixel Size
 *
 * @var integer
 */
define('PS',3);

/**
 * This class represents an image.
 *
 * This class makes it faster (yet, using more memory) to use images.
 * When a new instance is created and a $path is given the image will be read and parsed.
 * The colors will be automatically added to $dataHSV and $dataRGB. No futher conversions for colors will need to be done.
 * Sequential reads and writes to a certain pixel will not result in multiple shifts and look ups
 *
 * @author daantimmer
 */
class Image_Image{
	
	/**
	 * HSV data
	 * @var integer[]
	 */
	private $dataHSV = array();
	
	/**
	 * RGB data
	 * @var integer[]
	 */
	private $dataRGB = array();
	
	/**
	 * @deprecated can be removed?
	 * @var integer
	 */
	private $prev = 0;

	/**
	 * Image Width
	 * @var integer
	 */
	private $width = 0;
	
	/**
	 * Image Height
	 * @var integer
	 */
	private $height = 0;
	
	/**
	 * Constructor
	 *
	 * if no $path is given will create a new blank image with $width*$height as size. Else $width and $height will be discarded and
	 * the sizes of the image located in $path will be used.
	 *
	 * @param integer $width
	 * @param integer $height
	 * @param stirng $path
	 * @return void
	 */
	public function __construct($width,$height,$path = null){
//		echo __LINE__.":".memory_get_usage()."<br />\n";
		
		if($path !== null){
			
			$fp = fopen($path,"r");
			$data = fread($fp,filesize($path));
			$ip = imagecreatefromstring($data);
			$this->resetSize(imagesx($ip),imagesy($ip));
			for($y=0,$yn=imagesy($ip);$y<$yn;$y++){
				for($x=0,$xn=imagesx($ip);$x<$xn;$x++){
					$index = imagecolorat($ip, $x, $y);
					$rgb = imagecolorsforindex($ip,$index);
					$this->setColor($x, $y, new Image_Lib_Color(array($rgb["red"],$rgb["green"],$rgb["blue"])));
				}
			}
			imagedestroy($ip);
			$data = null;
		}else{
			$this->resetSize($width,$height);
		}
		
//		echo __LINE__.":".memory_get_usage()."<br />\n<br />\n";
	}

	/**
	 * Resets $dataHSV and $dataRGB to $width*$height*PS also sets $this->width and $this->height
	 *
	 * @param unknown_type $width
	 * @param unknown_type $height
	 * @return unknown_type
	 */
	public function resetSize($width,$height){
//		$this->dataHSV = array_fill(0,$width,array_fill(0,$height,array(rand(0,255),rand(0,255),rand(0,255))));
//		$this->dataRGB = array_fill(0,$width,array_fill(0,$height,array(rand(0,255),rand(0,255),rand(0,255))));

		$this->width = $width;
		$this->height = $height;
		
		$this->dataHSV = array_fill(0,$width*$height*PS,0);
		$this->dataRGB = array_fill(0,$width*$height*PS,0);
	}

	/**
	 * Returns height
	 *
	 * @return integer
	 */
	public function getHeight(){
//		return count($this->dataRGB[0]);
		return $this->height;
	}

	/**
	 * Returns width
	 *
	 * @return integer
	 */
	public function getWidth(){
//		return count($this->dataRGB);
		return $this->width;
	}

	/**
	 * Sets $color at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @param Image_Lib_Color $color
	 * @return void
	 */
	public function setColor($x,$y,Image_Lib_Color $color){
		$hsv = array(0,0,0);
		Image_Lib_Color::rgb2hsv($color->getRed(),$color->getGreen(), $color->getBlue(),$hsv);

		$this->setHSV($x, $y, $hsv);
		$this->setRGB($x, $y, array($color->getRed(),$color->getGreen(), $color->getBlue()));
	}

	/**
	 * Sets $hsv at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @param array $hsv
	 * @return void
	 */
	private function setHSV($x,$y,array $hsv){
//		$this->dataHSV[$x][$y] = $hsv;
		$offset = (($x) + ($y * $this->height)) * PS;
		list($this->dataHSV[$offset+H],
			$this->dataHSV[$offset+S],
			$this->dataHSV[$offset+V]) = $hsv;
	}

	/**
	 * Sets $rgb at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @param array $rgb
	 * @return void
	 */
	private function setRGB($x,$y,array $rgb){
//		$this->dataRGB[$x][$y] = $rgb;
		$offset = (($x) + ($y * $this->height)) * PS;
//		echo "({$x} * ".PS.") * ({$y} * {$this->height}) = {$offset}<br />\n";
		list($this->dataRGB[$offset+R],
			$this->dataRGB[$offset+G],
			$this->dataRGB[$offset+B]) = $rgb;
	}

	/**
	 * Returns Image_Lib_Color at ($x,$y)
	 *
	 * @param int $x
	 * @param int $y
	 * @return Image_Lib_Color
	 */
	public function getColorRGB($x,$y){
//		return new Image_Lib_Color($this->dataRGB[$x][$y]);
		$offset = (($x) + ($y * $this->height)) * PS;
		return new Image_Lib_Color(
			array(
				$this->dataRGB[$offset+R],
				$this->dataRGB[$offset+G],
				$this->dataRGB[$offset+B]));
	}

	/**
	 * Returns Image_Lib_Color after converting the HSV colors at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return Image_Lib_Color
	 */
	public function getColorHSV($x,$y){
//		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb($this->dataHSV[$x][$y]));
		$offset = (($x) + ($y * $this->height)) * PS;
		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(
			array(
				$this->dataHSV[$offset+H],
				$this->dataHSV[$offset+S],
				$this->dataHSV[$offset+V])));
	}

	/**
	 * Returns Image_Lib_Color color for HSV($H,1,1) at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return Image_Lib_Color
	 */
	public function getColorH($x,$y){
//		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(array($this->dataHSV[$x][$y][0],1,1)));
		$offset = (($x) + ($y * $this->height)) * PS;
		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(
			array(
				$this->dataHSV[$offset+H],
				1,
				1)));
	}

	/**
	 * Returns Image_Lib_Color color for HSV(1,$S,1) at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return Image_Lib_Color
	 */
	public function getColorS($x,$y){
//		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(array(1,$this->dataHSV[$x][$y][1],1)));
		$offset = (($x) + ($y * $this->height)) * PS;
		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(
			array(
				1,
				$this->dataHSV[$offset+S],
				1)));
	}

	/**
	 * Returns Image_Lib_Color color for HSV(1,1,$V) at ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return Image_Lib_Color
	 */
	public function getColorV($x,$y){
//		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(array(1,1,$this->dataHSV[$x][$y][2])));
		$offset = (($x) + ($y * $this->height)) * PS;
		return new Image_Lib_Color(Image_Lib_Color::hsv2rgb(
			array(
				1,
				1,
				$this->dataHSV[$offset+V])));
	}

	/**
	 * Returns Hue from ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return float
	 */
	public function getH($x,$y){
//		return $this->dataHSV[$x][$y][0];
		$offset = (($x) + ($y * $this->height)) * PS;
		return $this->dataHSV[$offset+H];
	}

	/**
	 * Returns Saturation from ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return float
	 */
	public function getS($x,$y){
//		return $this->dataHSV[$x][$y][1];
		$offset = (($x) + ($y * $this->height)) * PS;
		return $this->dataHSV[$offset+S];
	}

	/**
	 * Returns Value from ($x,$y)
	 *
	 * @param integer $x
	 * @param integer $y
	 * @return float
	 */
	public function getV($x,$y){
//		return $this->dataHSV[$x][$y][2];
		$offset = (($x) + ($y * $this->height)) * PS;
		return $this->dataHSV[$offset+V];
	}

	/**
	 * Saves $img to $path as png
	 *
	 * @param Image_Image $img
	 * @param string $path
	 * @return boolean
	 */
	public static function save(Image_Image $img, $path){
		$ip = imagecreatetruecolor($img->getWidth(),$img->getHeight());
		
		imagealphablending($ip, true);
		imagesavealpha($ip, true);

		for($y=0,$yn = $img->getHeight(); $y<$yn;$y++){
			for($x=0,$xn = $img->getWidth(); $x<$xn;$x++){

				$color = $img->getColorRGB($x,$y);
				
				if($color->getRed() == 0 && $color->getGreen() == 0 && $color->getBlue() == 0){
					$col = imagecolorallocate ($ip, 0xff, 0xff, 0xff);
				}else{
					$col = imagecolorallocatealpha ($ip,$color->getRed(),$color->getGreen(),$color->getBlue(), 0x0f);
				}
				
				imagesetpixel($ip, $x,$y, $col);

			}
		}
		
		return imagepng($ip,$path);
	}
}